#ifndef SANDBOX_SDK_MATH_VECTOR4_INTERNALS_HPP
#define SANDBOX_SDK_MATH_VECTOR4_INTERNALS_HPP

#ifdef _MSC_VER /* visual c++ */
# define ALIGN_BEG(xx) __declspec(align(xx))
# define ALIGN_END(xx) 
#else /* gcc or icc */
# define ALIGN_BEG(xx) 
# define ALIGN_END(xx) __attribute__((aligned(xx)))
#endif

#define QUAD(x) {x,x,x,x}

#include <x86intrin.h>

namespace sandbox
{
	namespace sdk
	{	
		namespace vector_constants
		{
			const ALIGN_BEG(16) __m128i sign_mask ALIGN_END(16);//topmost bits mask
			const ALIGN_BEG(16) __m128i sign_mask_xyz ALIGN_END(16);//same as above, but only for last 3 components(first is W)
			const ALIGN_BEG(16) __m128i ones ALIGN_END(16); //1
			const ALIGN_BEG(16) __m128  onesf ALIGN_END(16);//1.0
			const ALIGN_BEG(16) __m128  halff ALIGN_END(16);//0.5
		}
		namespace detail {
			template <int w, int x, int y, int z> inline const int shufle_mask()
			{
				STATIC_ASSERT(x>=0 && x<4);
				STATIC_ASSERT(y>=0 && y<4);
				STATIC_ASSERT(z>=0 && z<4);
				STATIC_ASSERT(w>=0 && w<4);
				return w|(x<<2)|(y<<4)|(z<<8);
			}
			//shuffle 1 vector
			template <typename S, int w, int x=w, int y=w, int z=w> 
			const S shuffle(const S s){return (S)_mm_shuffle_epi32((__m128i)s, shuffle_mask<w,x,y,z>());}
			template <typename S> 
			const S shuffle<S,0,1,2,3>(const S s){return (S)s;}
			//generic 2 argument shuffle
			//does not produce unsupported by hardware shuffles!!!!!
			template <typename S, int w, int x, int y, int z> 
			const S shuffle2(const S s1, const S s2){
				STATIC_ASSERT(x>=0 && x<4);
				STATIC_ASSERT(y>=0 && y<4);
				STATIC_ASSERT(z>=4 && z<8);
				STATIC_ASSERT(w>=4 && w<8);
				return (S)_mm_shuf_ps((__m128)s1, (__m128)s2,shuffle_mask<w,x,y-4,z-4>());
			}
			//some moves
			template <typename S> const S shuffle2<S,4,5,2,3>(const S s1, const S s2)
			{return (S)_mm_move_sd((__m128d)s1, (__m128d)s2);}
			template <typename S> const S shuffle2<S,0,1,6,7>(const S s1, const S s2)
			{return (S)_mm_move_sd((__m128d)s2, (__m128d)s1);}
			template <typename S> const S shuffle2<S,4,1,2,3>(const S s1, const S s2)
			{return (S)_mm_move_ss((__m128)s1, (__m128)s2);}
			template <typename S> const S shuffle2<S,0,5,6,7>(const S s1, const S s2)
			{return (S)_mm_move_ss((__m128)s2, (__m128)s1);}
			//unpacks-64bit
			template <typename S> const S shuffle2<S,0,1,4,5>(const S s1, const S s2)
			{return (S)_mm_unpacklo_epi64((__m128i)s1, (__m128i)s2);}
			template <typename S> const S shuffle2<S,4,5,0,1>(const S s1, const S s2)
			{return (S)_mm_unpacklo_epi64((__m128i)s2, (__m128i)s1);}
			template <typename S> const S shuffle2<S,2,3,6,7>(const S s1, const S s2)
			{return (S)_mm_unpackhi_epi64((__m128i)s1, (__m128i)s2);}
			template <typename S> const S shuffle2<S,6,7,2,3>(const S s1, const S s2)
			{return (S)_mm_unpackhi_epi64((__m128i)s2, (__m128i)s1);}u
			//unpacks-32bit
			template <typename S> const S shuffle2<S,2,6,3,7>(const S s1, const S s2)
			{return (S)_mm_unpacklo_epi32((__m128i)s1, (__m128i)s2);}
			template <typename S> const S shuffle2<S,6,2,7,3>(const S s1, const S s2)
			{return (S)_mm_unpacklo_epi32((__m128i)s2, (__m128i)s1);}
			template <typename S> const S shuffle2<S,0,4,1,5>(const S s1, const S s2)
			{return (S)_mm_unpackhi_epi32((__m128i)s1, (__m128i)s2);}
			template <typename S> const S shuffle2<S,4,0,5,1>(const S s1, const S s2)
			{return (S)_mm_unpackhi_epi32((__m128i)s2, (__m128i)s1);}
			//pshufw
			template <typename S> const S shuffle2<S,0,0,0,0>(const S s1, const S s2)
			{return (S)_mm_shuf_ps((__m128)s1, (__m128)s2, shuffle_mask(0,0,0,0));}
			template <typename S> const S shuffle2<S,1,0,0,0>(const S s1, const S s2)
			{return (S)_mm_shuf_ps((__m128)s1, (__m128)s2, shuffle_mask(1,0,0,0));}
			template <typename S> const S shuffle2<S,0,1,0,0>(const S s1, const S s2)
			{return (S)_mm_shuf_ps((__m128)s1, (__m128)s2, shuffle_mask(1,0,0,0));}
			
			void sincos_ps(__m128 x, __m128& cos, __m128& sin);	
		}
	}
};
#endif //SANDBOX_SDK_MATH_VECTOR4_INTERNALS_HPP

